/**
 *  Copyright (C) 2006 zhangbo (freeeob@gmail.com)
 *
 *  This product is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2.1 of the License, or
 *  (at your option) any later version.
 * 
 *  This product is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 * 
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 *
 *  author:zhangbo
 *  Email:jsjava@gmail.com
 */
 
 /**  The MathUtils class references to org.apache.commons.math.util.MathUtils */
 
function MathUtils(){
	this.jsjava_class="jsorg.apache.commons.math.util.MathUtils";
}

/**
 * Returns an exact representation of the Binomial Coefficient, "n choose k", the 
 * number of k-element subsets that can be selected from an n-element set.
 * @param n
 * @param k
 */
MathUtils.binomialCoefficient=function(n,k){
	if (n < k) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,"must have n >= k for binomial coefficient (n,k)");
    }
    if (n < 0) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,"must have n >= 0 for binomial coefficient (n,k)");
    }
    if ((n == k) || (k == 0)) {
        return 1;
    }
    if ((k == 1) || (k == n - 1)) {
        return n;
    }
    var result = Math.round(MathUtils.binomialCoefficientDouble(n, k));
    if (result == Long.MAX) {
        throw new ArithmeticException(ArithmeticException.ERROR,"result too large to represent in a long integer");
    }
    return result;
};

/**
 * Returns an double representation of the Binomial Coefficient, "n choose k", the 
 * number of k-element subsets that can be selected from an n-element set.
 * @param n
 * @param k
 */
MathUtils.binomialCoefficientDouble=function(n,k){
	return Math.floor(Math.exp(MathUtils.binomialCoefficientLog(n, k)) + 0.5);
};

/**
 * Returns the natural log of the Binomial Coefficient, "n choose k", the 
 * number of k-element subsets that can be selected from an n-element set.
 * @param n
 * @param k
 */
MathUtils.binomialCoefficientLog=function(n,k){
	if (n < k) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,"must have n >= k for binomial coefficient (n,k)");
    }
    if (n < 0) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,"must have n >= 0 for binomial coefficient (n,k)");
    }
    if ((n == k) || (k == 0)) {
        return 0;
    }
    if ((k == 1) || (k == n - 1)) {
        return Math.log(n);
    }
    var logSum = 0;

    for (var i = k + 1; i <= n; i++) {
        logSum += Math.log(i);
    }

    for (var i = 2; i <= n - k; i++) {
        logSum -= Math.log(i);
    }

    return logSum;
};

/**
 * Returns the hyperbolic cosine of x.
 * @param x
 */
MathUtils.cosh=function(x){
	return (Math.exp(x) + Math.exp(-x)) / 2.0;
};

/**
 * Returns n!.
 * @param n
 */
MathUtils.factorial=function(n){
	var result = Math.round(MathUtils.factorialDouble(n));
    if (result == Long.MAX) {
        throw new ArithmeticException(ArithmeticException.ERROR,"result too large to represent in a long integer");
    }
    return result;
};

/**
 * Returns n!.
 * @param n
 */
MathUtils.factorialDouble=function(n){
	if (n < 0) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,"must have n >= 0 for n!");
    }
    return Math.floor(Math.exp(MathUtils.factorialLog(n)) + 0.5);
};

/**
 * Returns n!.
 * @param n
 */
MathUtils.factorialLog=function(n){
	if (n < 0) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,"must have n > 0 for n!");
    }
    var logSum = 0;
    for (var i = 2; i <= n; i++) {
        logSum += Math.log(i);
    }
    return logSum;
};

/**
 * Gets the greatest common divisor of 
 * the absolute value of two numbers, using the "binary gcd" method which avoids division and modulo operations.
 * @param u
 * @param v
 */
MathUtils.gcd=function(u,v){
	if (u * v == 0) {
        return (Math.abs(u) + Math.abs(v));
    }
    if (u > 0) {
        u = -u;
    } 
    if (v > 0) {
        v = -v;
    } 
    var k = 0;
    while ((u & 1) == 0 && (v & 1) == 0 && k < 31) {
                                                        
        u /= 2;
        v /= 2;
        k++; 
    }
    if (k == 31) {
        throw new ArithmeticException(ArithmeticException.ERROR,"overflow: gcd is 2^31");
    }
    var t = ((u & 1) == 1) ? v : -(u / 2);
    do {
        while ((t & 1) == 0) { 
            t /= 2; 
        }
        if (t > 0) {
            u = -t;
        } else {
            v = t;
        }
        t = (v - u) / 2;
    } while (t != 0);
    return -u * (1 << k); 
};

/**
 * For a value x, this method returns +1 if x >= 0 and -1 if x < 0.
 * @param x
 */
MathUtils.indicator=function(x){
	return (x >= 0) ? 1 : -1;
};

/**
 * Returns the least common multiple between two integer values.
 * @param a
 * @param b
 */
MathUtils.lcm=function(a,b){
 	var x=a/MathUtils.gcd(a,b);
 	var y=b;
	var m =x*y;
	return Math.abs(m);
};

/**
 * Returns the sign for value x.
 * @param x
 */
MathUtils.sign=function(x){
	return (x == 0) ? 0 : (x > 0) ? 1 : -1;
};

/**
 * Returns the hyperbolic sine of x.
 * @param x
 */
MathUtils.sinh=function(x){
	return (Math.exp(x) - Math.exp(-x)) / 2.0;
};